package com.xxx.oauth.client;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.util.StringUtils;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * OAuth2认证filter
 */
public class OAuth2AuthenticationFilter extends AuthenticatingFilter {

	// oauth2 authc code参数名
	private String authcCodeParam = "code"; // oauth2的授权码方式
	
	// 认证失败跳转url
	private String failureUrl;

	public void setAuthcCodeParam(String authcCodeParam) {
		this.authcCodeParam = authcCodeParam;
	}
	
	public void setFailureUrl(String failureUrl) {
		this.failureUrl = failureUrl;
	}

	@Override
	protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception {
		HttpServletRequest httpRequest = (HttpServletRequest) request;
		String code = httpRequest.getParameter(authcCodeParam);
		return new OAuth2Token(code);
	}

	@Override
	protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
		return false;
	}

	/**
		1、首先判断有没有服务端返回的error参数，如果有则直接重定向到失败页面；
		2、接着如果用户还没有身份验证，判断是否有auth code参数（即是不是服务端授权之后返回的），如果没有则重定向到服务端进行授权；
		3、否则调用executeLogin进行登录，通过auth code创建OAuth2Token 提交给Subject进行登录；
		4、登录成功将回调onLoginSuccess方法重定向到成功页面；
		5、登录失败则回调onLoginFailure 重定向到失败页面。
	 */
	@Override
	protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
		String error = request.getParameter("error");
		String errorDescription = request.getParameter("error_description");
		if (!StringUtils.isEmpty(error)) {// 如果服务端返回了错误
			WebUtils.issueRedirect(request, response,
					failureUrl + "?error=" + error + "error_description=" + errorDescription);
			return false;
		}

		// 获取登录用户信息
		Subject subject = getSubject(request, response);
		if (!subject.isAuthenticated()) { // 未认证
			if (StringUtils.isEmpty(request.getParameter(authcCodeParam))) {
				// 如果用户没有身份验证，且没有auth code，则重定向到服务端授权
				saveRequestAndRedirectToLogin(request, response);
				return false;
			}
		}

		// 执行登录流程
		return executeLogin(request, response);
	}

	/**
	 * 登录成功后的回调方法重定向到成功页面
	 */
	@Override
	protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request,
			ServletResponse response) throws Exception {
		issueSuccessRedirect(request, response);
		return false;
	}

	/**
	 * 登录失败后的回调
	 */
	@Override
	protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException ae,
			ServletRequest request, ServletResponse response) {
		Subject subject = getSubject(request, response);
		if (subject.isAuthenticated() || subject.isRemembered()) { // 为认证 || 没有“记住我”
			try {
				// 如果身份验证成功了则也重定向到成功页面
				issueSuccessRedirect(request, response);
			} catch (Exception e) {
				e.printStackTrace();
			}
		} else {
			try {
				// 登录失败时重定向到认证失败页面
				WebUtils.issueRedirect(request, response, failureUrl);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return false;
	}

}
